package main

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

/**
在Go语言中，一个map就是一个哈希表的应用，map类型可以写为map[k]v ，其中K和V分别对应的key和value，map中所有的key都有相同的类型,
所有的value也有相同的类型，，但是key和value之间可以是不同的数据类型，其中key对应的K必须是支持==比较运算符的数据类型，
所以map可以通过测试key是否相等来判断是否已经存在，虽然浮点数据类型也支持等运算符比较的，但是将浮点数用作key类型则是一个坏的想法，
最坏的情况是可能出现的NaN 和任何任何浮点数都不相等。对于V对应的value 数据类型没有任何的限制。
*/

/**
Map是引用类型
Go语言中的map是引用类型，必须初始化才能使用。
为什么引用类型必须初始化才能使用呢？请看new 和make 函数

声明Map

可以使用内建函数 make 也可以使用 map 关键字来声明Map
	//声明
	var map_name map[key_type]value_type
	//初始化或者说是创建
	map_name = make(map[key_type]value_type)


	//直接使用 make 函数进行声明+创建
	map_name := make(map[key_type]value_type)
key_type 表示键的数据类型，value_type 表示键对应的值的数据类型

map是引用类型，如果只声明，而不创建map（用map函数创建map），那么就会创建一个nil map
nil map 不能存放键值对，如果对nil 进行操作会报错。

声明之后，map类型的变量默认初始值为nil，需要使用make()函数，来分配内存。语法为：
make(map[key_type]value_type,[cap])
其中 cap 表示map的容量，该参数虽然不是必须的，但是我们应该在初始化map的时候就为其指定一个合适的容量。

初始化Map
map中的数据都是成对出现的，有一下两种初始化map的方式：
方式一：
声明+初始化
	map_name := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
方式二：
	countryCapitalMap["France"] = "巴黎"
	countryCapitalMap["Italy"] = "罗马"
	countryCapitalMap["Japan"] = "东京"
	countryCapitalMap["India "] = "新德里"



*/

func main() {
	fmt.Println("hello")
	var myMap = make(map[string]int)
	myMap["name"] = 123
	myMap["age"] = 20
	fmt.Printf("输出集合的数据类型 : %T\n", myMap)
	fmt.Println(myMap)
	fmt.Println(myMap["name"])
	fmt.Println("=================判断集合中某个键是否存在==================")
	/**
	Go语言中有个判断map中某个键是否存在的特殊写法，格式如下：value,ok:=map[key]
	如果键存在，那么会返回 相应的值 和  true
	如果键不存在，那么会返回 空 和 false
	*/
	capital, v := myMap["name"]
	fmt.Println(capital, v)
	capital1, v1 := myMap["student"]
	fmt.Println(capital1, v1)

	fmt.Println("==================delete()函数====================")
	/**
	delete 函数用户删除集合中的元素，参数为某个map和其中的某个key
	使用delete()函数从map中删除一组键值对，delete()函数的格式如下：delete(map,key)
	其中map表示要删除键值对的map, key  表示要删除键值对的键
	*/
	fmt.Println(myMap)
	delete(myMap, "name")
	fmt.Println(myMap)

	fmt.Println("==================遍历map无序（默认）====================")
	//遍历集合中的key 和 value
	for k, v := range myMap {
		if v == 123 {
			v = 21
		}
		fmt.Println("k:", k, "v:", v)
	}
	//遍历集合只获取集合中的key
	for k := range myMap {
		fmt.Println("k:", k)
	}
	/*
		1.如果使用range遍历集合的是偶吗，range会按顺序返回两个值：键和值。
		  如果只有一接收者的话，默认接受的是键（key）
		2.遍历map的时候元素的顺序与添加键值对的顺序无关
	*/

	//有序遍历map
	/**
	要实现map的有序遍历，需要对键进行排序，再按照键的顺序输出值
	对键进行排序的方法是把所有键放在一个切片里，然后用sort包中的函数进行排序
	*/
	fmt.Println("==================有序遍历map====================")
	rand.Seed(time.Now().UnixNano()) //初始化随机数种子

	scoreMap := make(map[string]int, 100)
	for i := 0; i < 50; i++ {
		key := fmt.Sprintf("stu%02d", i)
		value := rand.Intn(50)
		scoreMap[key] = value
	}
	fmt.Println("--------直接遍历------")
	for key := range scoreMap {
		fmt.Println(key, scoreMap[key])
	}
	fmt.Println("-------排序后遍历---------")
	//去除map中所有的key存入切片keys中
	keys := make([]string, 0, 100)
	for key := range scoreMap {
		keys = append(keys, key)
	}
	sort.Strings(keys) //对切片进行排序
	//按照排序后的key遍历map
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}
	fmt.Println("==================复杂数据类型====================")

	fmt.Println("==元素为map类型的切片===")
	// mapSlice是一个元素为map类型的切片
	var mapSlice = make([]map[string]string, 3)
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
	fmt.Println("after init")
	// 对切片中的map元素进行初始化
	mapSlice[0] = make(map[string]string, 10)
	mapSlice[0]["name"] = "王五"
	mapSlice[0]["password"] = "123456"
	mapSlice[0]["address"] = "红旗大街"
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
	fmt.Println("==值为切片类型的map===")
	//声明+创建 map，这个map的键是string类型，值是[]string切片类型
	var sliceMap = make(map[string][]string, 3)
	fmt.Println(sliceMap)

	fmt.Println("after init")
	key := "中国"
	value, ok := sliceMap[key]
	if !ok { //如果sliceMap中不存在这个键
		value = make([]string, 0, 2)
	}
	value = append(value, "北京", "上海")
	sliceMap[key] = value
	fmt.Println(sliceMap)

}
